package parser;

import java.util.ArrayList; 
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Queue;
import java.util.Stack;
import java.util.StringTokenizer;

import model.Model;
import nodes.AbstractNode;
import nodes.command.CommandNode;
import resourceManager.ResourceManager;
import tokens.AbstractBinaryToken;
import tokens.AbstractToken;
import tokens.CommandToken;
import reflection.Reflection;

/**
 * Uses the Shunting-Yard algorithm to create an expression tree
 * out of user input
 * 
 * @author Charlie Hatcher
 * @author Andrew
 */
public class Parser
{
    public static final String TOKEN_DELIMITERS = "+-*/[],!$ ";
    private List<AbstractToken> myPossibleTokens = new ArrayList<AbstractToken>();
    private static Model myModel;


    public Parser (Model model)
    {
        myModel = model;
        ResourceManager resources = ResourceManager.getInstance();
        resources.addResourcesFromFile("syntaxToTokens");
        for (String key : resources.getKeySet())
        {
            myPossibleTokens.add((AbstractToken) Reflection.createInstance(resources.getString(key)));
        }
        AbstractToken.setParser(this);
        AbstractToken.setPossibleTokens(myPossibleTokens);
    }
    
    
    /**
     * Breaks a string into its tokens, based on TOKEN_DELIMITERS
     * Calls makeExpression(ListIterator)
     */
    public List<AbstractNode>  makeExpression (String infix) throws Exception
    {
        StringTokenizer tokenizer = new StringTokenizer(infix, TOKEN_DELIMITERS, false);
        LinkedList<String> infixList = new LinkedList<String>();
        AbstractToken.setModel(myModel);
        while (tokenizer.hasMoreTokens())
        {
            infixList.add(tokenizer.nextToken());
        }
        return makeExpression(infixList.listIterator());
    }

    /**
     * Creates an expression tree out of a list iterator
     */
    private List<AbstractNode> makeExpression (ListIterator<String> it) throws Exception
    {
        Stack<AbstractBinaryToken> opStack = new Stack<AbstractBinaryToken>();
        Stack<AbstractNode> stack = new Stack<AbstractNode>();
        Queue<AbstractNode> expQueue = new LinkedList<AbstractNode>();
        List<AbstractNode> rootList = new LinkedList<AbstractNode>();
        Queue<AbstractNode> outputQueue = new LinkedList<AbstractNode>();
        String token = "";

        // Iterate through all tokens of the string.
        while (it.hasNext())
        {
            token = it.next().trim();

            /*
             * decideTokenType returns the type of that token. The handleParser
             * function will then build the correspond node and put it on the
             * stack. For example, if the AddToken's handleParser is called, it
             * will build an AddNode and push it onto the stack.
             */
            decideTokenType(token, it).handleParser(token, it, opStack, outputQueue, expQueue);
        }

        // Add remaining operators to the stack.
        while (!opStack.empty())
        {   
            stack.push(opStack.pop().process());
        }

                
        // Used to support unlimited parameter passing.
        // Will cause an exception in the executor.
        if (outputQueue.isEmpty()) throw ParserException.NO_INPUT;

        // The AbstractNode implements a buildTree function which will
        // build an expression recursively.
        while (!outputQueue.isEmpty()){
        	rootList.add(outputQueue.poll().buildTree(outputQueue));
        }        
        return rootList;
    }


    /**
     * Iterate through all possible tokens and return the matched type.
     */
    public AbstractToken decideTokenType (String token, ListIterator<String> it)
    throws Exception
    {
        for (AbstractToken tmpToken : myPossibleTokens)
        {
            if (tmpToken.matchToken(token, it))
            {
                return tmpToken;
            }
        }
       throw ParserException.BAD_TOKEN;
    }
}
